package com.yf.monitor.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.yf.monitor.annotatior.ApiMonitor;
import com.yf.monitor.entity.ApiRecord;
import com.yf.monitor.service.ApiRecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName ApiMonitorAspect
 * @Description 切面
 * @Author Scott.Yin
 * @Date 2025/8/21 17:29
 * @Version 1.0
 **/
@Aspect
@Component
@Slf4j
public class ApiMonitorAspect {

    private final ApiRecordService apiRecordService;
    private final Random random = new Random();
    private final ThreadLocal<ApiRecord> recordThreadLocal = new ThreadLocal<>();
    // 1. 核心组件：线程安全的ObjectMapper（避免重复创建）
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    // 2. 敏感字段配置（可扩展为配置文件）
    private static final Set<String> SENSITIVE_FIELDS = new HashSet<>(
            Arrays.asList("password", "pwd")
    );
    private static final LocalVariableTableParameterNameDiscoverer PARAM_NAME_DISCOVERER =
            new LocalVariableTableParameterNameDiscoverer();

    @Autowired
    public ApiMonitorAspect(ApiRecordService apiRecordService) {
        this.apiRecordService = apiRecordService;
    }

    @Pointcut("@annotation(com.yf.monitor.annotatior.ApiMonitor)")
    public void apiMonitorPointcut() {

    }

    @Before("apiMonitorPointcut() && @annotation(apiMonitor)")
    public void doBefore(JoinPoint joinPoint, ApiMonitor apiMonitor) {
        // 采样判断
        if (apiMonitor.sampleRate() < 1.0 && random.nextDouble() > apiMonitor.sampleRate()) {
            return;
        }

        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes)
                    RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return;
            }

            HttpServletRequest request = attributes.getRequest();

            ApiRecord record = ApiRecord.builder()
                    .traceId(UUID.randomUUID().toString())
                    .startTime(System.currentTimeMillis())
                    .apiName(StringUtils.isNotBlank(apiMonitor.value()) ?
                            apiMonitor.value() : joinPoint.getSignature().toShortString())
                    .method(request.getMethod())
                    .uri(request.getRequestURI())
                    .remoteAddr(request.getRemoteAddr())
                    .serverAddr(request.getLocalAddr())
                    .userAgent(request.getHeader("User-Agent"))
                    .build();

            // 3. 构建脱敏后的参数Map
            Map<String, Object> paramsMap = buildSafeParameterMap(joinPoint);
            if (paramsMap.isEmpty()) {
                return;
            }
            // 4. 转换为JSON字符串
            String paramsJson = OBJECT_MAPPER.writeValueAsString(paramsMap);
            // 记录请求参数
            if (apiMonitor.logRequest()) {
                if (!CollectionUtils.isEmpty(paramsMap)) {
                    record.setRequestBody(paramsJson);
                }
            }

            //关键字解析
            if (apiMonitor.keyword() != null && StringUtils.isNotBlank(apiMonitor.keyType())) {
                StringBuilder sb = new StringBuilder(apiMonitor.keyType());
                for (String keyParam : apiMonitor.keyword()) {
                    JsonNode jsonNode = OBJECT_MAPPER.readTree(paramsJson);
                    String valueByKey = findValueByKey(jsonNode, keyParam);
                    if (valueByKey == null && keyParam.contains(".")) {
                        //最多两级查找
                        String[] splitKey = keyParam.split("\\.");
                        String value = findValueByKey(jsonNode, splitKey[0]);
                        if (value == null) {
                            continue;
                        }

                        JSONObject spObj = JSON.parseObject(value);
                        if (spObj == null) {
                            continue;
                        }
                        valueByKey = spObj.getString(splitKey[1]);
                    }
                    if (valueByKey == null) {
                        continue;
                    }
                    sb.append("-").append(valueByKey);
                }
                record.setKeyword(sb.toString());
            }

            recordThreadLocal.set(record);
        } catch (Exception e) {
            log.warn("API监控前置处理异常", e);
        }
    }

    @AfterReturning(pointcut = "apiMonitorPointcut() && @annotation(apiMonitor)",
            returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, ApiMonitor apiMonitor, Object result) {
        try {
            ApiRecord record = recordThreadLocal.get();
            if (record == null) return;

            record.setEndTime(System.currentTimeMillis());
            record.setDuration(record.getEndTime() - record.getStartTime());
            record.setSuccess(true);

            // 记录响应结果
            if (apiMonitor.logResponse() && result != null) {
                try {
                    String response = JSON.toJSONString(result);
                    // 截断过长的响应内容
                    record.setResponseBody(StringUtils.substring(response, 0, 2000));
                } catch (Exception e) {
                    log.warn("序列化响应结果失败", e);
                }
            }


            // 异步或同步处理记录
            if (apiMonitor.async()) {
                apiRecordService.asyncSaveRecord(record);
            } else {
                apiRecordService.saveRecord(record);
            }
        } catch (Exception e) {
            log.warn("API监控返回处理异常", e);
        } finally {
            recordThreadLocal.remove();
        }
    }

    @AfterThrowing(pointcut = "apiMonitorPointcut() && @annotation(apiMonitor)",
            throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, ApiMonitor apiMonitor, Exception e) {
        try {
            ApiRecord record = recordThreadLocal.get();
            if (record == null) return;

            record.setEndTime(System.currentTimeMillis());
            record.setDuration(record.getEndTime() - record.getStartTime());
            record.setSuccess(false);
            record.setErrorMsg(e.getMessage());
            record.setErrorStack(ExceptionUtils.getStackTrace(e));

            // 异步或同步处理记录
            if (apiMonitor.async()) {
                apiRecordService.asyncSaveRecord(record);
            } else {
                apiRecordService.saveRecord(record);
            }
        } catch (Exception ex) {
            log.warn("API监控异常处理异常", ex);
        } finally {
            recordThreadLocal.remove();
        }
    }

    /**
     * @Description 内容转换
     * @Author Scott.Yin
     * @Date 2025/8/28 16:48
     * @Version 1.0
     **/
    private Map<String, Object> buildSafeParameterMap(JoinPoint joinPoint) {
        Map<String, Object> safeParams = new LinkedHashMap<>(); // 保持参数顺序
        Object[] args = joinPoint.getArgs();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        String[] paramNames = PARAM_NAME_DISCOVERER.getParameterNames(method);

        // 处理参数名缺失情况
        if (paramNames == null) {
            paramNames = new String[args.length];
            for (int i = 0; i < args.length; i++) {
                paramNames[i] = "arg" + i;
            }
        }

        // 7. 核心逻辑：参数脱敏 + 安全序列化
        for (int i = 0; i < args.length; i++) {
            String paramName = paramNames[i];
            Object paramValue = args[i];

            // 8. 敏感字段脱敏（不修改原始数据）
            if (isSensitiveField(paramName)) {
                safeParams.put(paramName, "******");
                continue;
            }

            // 9. 处理特殊类型（文件上传等）
            if (paramValue != null && isFileUploadType(paramValue.getClass())) {
                safeParams.put(paramName, "[MultipartFile: " + paramValue.getClass().getSimpleName() + "]");
                continue;
            }

            // 处理Servlet API对象（HttpServletRequest, HttpServletResponse等）
            if (isServletObject(paramValue)) {
                safeParams.put(paramName, "[ServletObject: " + paramValue.getClass().getSimpleName() + "]");
                continue;
            }

            // 处理其他可能不可序列化的对象
            if (paramValue != null && !isSerializable(paramValue)) {
                safeParams.put(paramName, "[Unserializable: " + paramValue.getClass().getSimpleName() + "]");
                continue;
            }

            // 10. 安全添加参数（避免序列化破坏）
            safeParams.put(paramName, paramValue);
        }
        return safeParams;
    }

    // 敏感字段检测（忽略大小写）
    private boolean isSensitiveField(String fieldName) {
        return SENSITIVE_FIELDS.stream()
                .anyMatch(s -> s.equalsIgnoreCase(fieldName));
    }

    // 识别文件上传类型（避免序列化失败）
    private boolean isFileUploadType(Class<?> clazz) {
        return clazz.getName().contains("MultipartFile") ||
                clazz.getName().contains("MultipartFile");
    }

    // 判断是否为Servlet API对象
    private boolean isServletObject(Object obj) {
        if (obj == null) return false;
        String className = obj.getClass().getName();
        return className.startsWith("javax.servlet.") ||
                className.startsWith("jakarta.servlet.") ||
                className.contains("HttpServletRequest") ||
                className.contains("HttpServletResponse") ||
                className.contains("RequestFacade") ||
                className.contains("ResponseFacade");
    }

    // 检查对象是否可序列化
    private boolean isSerializable(Object obj) {
        if (obj == null) return true;

        // 基本类型和字符串总是可序列化的
        if (obj instanceof String || obj instanceof Number ||
                obj instanceof Boolean || obj instanceof Character) {
            return true;
        }

        // 检查是否是常见集合类型（需要递归检查内容）
        if (obj instanceof Collection) {
            for (Object item : (Collection<?>) obj) {
                if (!isSerializable(item)) return false;
            }
            return true;
        }

        if (obj instanceof Map) {
            for (Map.Entry<?, ?> entry : ((Map<?, ?>) obj).entrySet()) {
                if (!isSerializable(entry.getKey()) || !isSerializable(entry.getValue())) {
                    return false;
                }
            }
            return true;
        }

        // 检查是否有@JsonIgnoreProperties或@JsonSerialize注解
        Class<?> clazz = obj.getClass();
        if (clazz.isAnnotationPresent(JsonIgnoreProperties.class) ||
                clazz.isAnnotationPresent(JsonSerialize.class)) {
            return true;
        }

        // 对于其他对象类型，使用Jackson尝试序列化来检查
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.writeValueAsString(obj);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * 递归查找JSON中指定键的值
     * @param node 当前JSON节点
     * @param key 要查找的键
     * @return 找到的值，如果未找到则返回null
     */
    public static String findValueByKey(JsonNode node, String key) {
        // 如果当前节点是对象，遍历所有字段
        if (node.isObject()) {
            // 检查当前对象是否包含目标键
            if (node.has(key)) {
                JsonNode valueNode = node.get(key);
                if (valueNode != null && valueNode.isValueNode()) {
                    return valueNode.asText();
                }
            }

            // 递归检查所有字段
            for (JsonNode child : node) {
                String result = findValueByKey(child, key);
                if (result != null) {
                    return result;
                }
            }
        }
        // 如果当前节点是数组，遍历所有元素
        else if (node.isArray()) {
            for (JsonNode child : node) {
                String result = findValueByKey(child, key);
                if (result != null) {
                    return result;
                }
            }
        }

        return null;
    }
}
